﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenTK;
using DDD.Core;
using DDD.OpenTK;
using FloatComparer = DDDTest.Helper.FloatComparer;

namespace DDDTest.OpenTK.TestUniform {
    [TestClass]
    public class TestUniform_sampler2D_ {

        #region Field
        GameWindow window;
        World wld;
        Graphics3D g3d;
        FrameBuffer fbuf;
        #endregion

        #region Setup
        [TestInitialize ()]
        public void MyClassInitialize () {
            window = new GameWindow ();
            wld = CreateWorld ();
            fbuf = CreateFrameBuffer ();

            g3d = Graphics3D.GetInstance ();
            g3d.Reset ();

            g3d.RenderPass += delegate {
                g3d.SetFrameBuffer (fbuf);
                g3d.SetWorld (wld);
                g3d.SetViewort (0, 0, 1, 1);
            };
        }

        [TestCleanup ()]
        public void MyClassCleanup () {
            window.Dispose ();
            window = null;
            wld = null;
        }

        private static FrameBuffer CreateFrameBuffer () {
            var fbuf = new FrameBuffer (8, 1, 0);
            var color0 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var color1 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var color2 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var color3 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var color4 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var color5 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var color6 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var color7 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            fbuf.SetColorBuffer (0, color0);
            fbuf.SetColorBuffer (1, color1);
            fbuf.SetColorBuffer (2, color2);
            fbuf.SetColorBuffer (3, color3);
            fbuf.SetColorBuffer (4, color4);
            fbuf.SetColorBuffer (5, color5);
            fbuf.SetColorBuffer (6, color6);
            fbuf.SetColorBuffer (7, color7);
            return fbuf;
        }

        private World CreateWorld () {
            var cam = new Camera ();
            cam.SetPerspective (45, 1, 0.1f, 10f);

            var camNode = new Node ();
            camNode.Attach (cam);
            camNode.SetTranslation (0, 0, 1);

            var pos = new float[3] { 0, 0, 0 };
            var posArray = new VertexArray<float> (1, 3);
            posArray.Set (0, 1, pos);

            var vbuf = new VertexBuffer ();
            vbuf.AddVertexArray ("v_Position", posArray);

            var ibuf = new IndexBuffer<int> (PrimitiveType.Point, 1);
            var indices = new int[] { 0 };
            ibuf.Set (0, 1, indices);

            var vert = @"#version 400
                         in  vec3 v_Position;
                               
                         void main () {
                             gl_Position = vec4(v_Position, 1);
                         }
                        ";
            var frag = @"#version 400
                         uniform sampler2D Texture0;
                         uniform sampler2D Texture1[2];
                         uniform sampler2D Texture2;
                         uniform sampler2D Texture3[2];

                         out vec4  frag_Color0;
                         out vec4  frag_Color1;
                         out vec4  frag_Color2;
                         out vec4  frag_Color3;
                         out vec4  frag_Color4;
                         out vec4  frag_Color5;

                         void main () {
                             frag_Color0 = texture(Texture0   , vec2(0.5f, 0.5f));
                             frag_Color1 = texture(Texture1[0], vec2(0.5f, 0.5f));
                             frag_Color2 = texture(Texture1[1], vec2(0.5f, 0.5f));
                             frag_Color3 = texture(Texture2   , vec2(0.5f, 0.5f));
                             frag_Color4 = texture(Texture3[0], vec2(0.5f, 0.5f));
                             frag_Color5 = texture(Texture3[1], vec2(0.5f, 0.5f));
                         }
                        ";

            var vs = new VertexShader (new MemoryStream (Encoding.UTF8.GetBytes (vert)));
            var fs = new FragmentShader (new MemoryStream (Encoding.UTF8.GetBytes (frag)));
            var sp = new ShaderProgram (vs, fs);

            var tex0 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var tex1 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var tex2 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var tex3 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var tex4 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            var tex5 = new Image2D<float> (PixelFormat.RGBA32f, 1, 1);
            tex0.Set (0, 0, 1, 1, new float[] { 1, 2, 3, 4 });
            tex1.Set (0, 0, 1, 1, new float[] { 5, 6, 7, 8 });
            tex2.Set (0, 0, 1, 1, new float[] { 9, 10, 11, 12 });
            tex3.Set (0, 0, 1, 1, new float[] { 13, 14, 15, 16 });
            tex4.Set (0, 0, 1, 1, new float[] { 17, 18, 19, 20});
            tex5.Set (0, 0, 1, 1, new float[] { 21, 22, 23, 24 });

            var smp0 = new Sampler (SamplerType.Texture2D);
            var smp1 = new Sampler (SamplerType.Texture2D);
            var smp2 = new Sampler (SamplerType.Texture2D);
            var smp3 = new Sampler (SamplerType.Texture2D);
            var smp4 = new Sampler (SamplerType.Texture2D);
            var smp5 = new Sampler (SamplerType.Texture2D);
            smp0.SetImage (tex0);
            smp1.SetImage (tex1);
            smp2.SetImage (tex2);
            smp3.SetImage (tex3);
            smp4.SetImage (tex4);
            smp5.SetImage (tex5);

            var mat = new Helper.MyTestMaterial ();
            mat.Sampler = smp3;
            mat.ArraySampler = new Sampler[2] { smp4, smp5 };

            var unifs = new UniformArray ();
            unifs.Add ("Texture0"   , smp0);                           // 2Dテクスチャー
            unifs.Add ("Texture1[0]", new Sampler[] { smp1, smp2 });   // 2Dテクスチャー(配列)
            unifs.Add ("Texture2"   , mat, () => mat.Sampler);         // プロパティ、2Dテクスチャー
            unifs.Add ("Texture3[0]", mat, () => mat.ArraySampler);    // プロパティ、2Dテクスチャー(配列)

            var app = new Appearance ();
            app.SetShaderProgram (sp);
            app.AddUniformArray (unifs);
            app.SetMaterial (mat);
            app.AddSampler (smp0);
            app.AddSampler (smp1);
            app.AddSampler (smp2);
            app.AddSampler (smp3);
            app.AddSampler (smp4);
            app.AddSampler (smp5);

            var msh = new Mesh ();
            msh.SetVertexBuffer (vbuf);
            msh.SetIndexBuffer (ibuf);
            msh.SetAppearance (app);

            var mshNode = new Node ();
            mshNode.Attach (msh);

            var wld = new World ();
            wld.AddChild (camNode);
            wld.AddChild (mshNode);
            wld.SetActiveCamera (camNode);

            return wld;
        }

        #endregion



        #region TestMethod
        [TestMethod]
        public void TestMethod1 () {

            g3d.Render ();
            window.SwapBuffers ();

            var expected0 = new float[4] { 1, 2, 3, 4 };
            var expected1 = new float[4] { 5, 6, 7, 8 };
            var expected2 = new float[4] { 9, 10, 11, 12 };
            var expected3 = new float[4] { 13, 14, 15, 16 };
            var expected4 = new float[4] { 17, 18, 19, 20 };
            var expected5 = new float[4] { 21, 22, 23, 24 };
            var pixels0 = new float[4];
            var pixels1 = new float[4];
            var pixels2 = new float[4];
            var pixels3 = new float[4];
            var pixels4 = new float[4];
            var pixels5 = new float[4];
            var color0 = fbuf.GetColorBuffer (0) as Image2D<float>;
            var color1 = fbuf.GetColorBuffer (1) as Image2D<float>;
            var color2 = fbuf.GetColorBuffer (2) as Image2D<float>;
            var color3 = fbuf.GetColorBuffer (3) as Image2D<float>;
            var color4 = fbuf.GetColorBuffer (4) as Image2D<float>;
            var color5 = fbuf.GetColorBuffer (5) as Image2D<float>;
            color0.Capture ();
            color1.Capture ();
            color2.Capture ();
            color3.Capture ();
            color4.Capture ();
            color5.Capture ();
            color0.Get (0, 0, 1, 1, pixels0);
            color1.Get (0, 0, 1, 1, pixels1);
            color2.Get (0, 0, 1, 1, pixels2);
            color3.Get (0, 0, 1, 1, pixels3);
            color4.Get (0, 0, 1, 1, pixels4);
            color5.Get (0, 0, 1, 1, pixels5);

            CollectionAssert.AreEqual (expected0, pixels0);
            CollectionAssert.AreEqual (expected1, pixels1);
            CollectionAssert.AreEqual (expected2, pixels2);
            CollectionAssert.AreEqual (expected3, pixels3);
            CollectionAssert.AreEqual (expected4, pixels4);
            CollectionAssert.AreEqual (expected5, pixels5);
        }
        #endregion



    }
}
